/*
 * File: CFdEventKQueue.cpp
 * Copyright (c) 2010. Silviu Caragea <silviu@intelliproject.net>
 * Created on Octomber 12, 2010, 12:34 AM
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  1.Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  2.Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  3.All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *       This product includes software developed by the University of
 *       California, Berkeley and its contributors.
 *  4.Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include "CFdEventKQueue.h"

#ifdef USE_KQUEUE

namespace litevent
{

int CFdEventKQueue::setEvent(int fd_node , int fd, int events)
{
    UNUSED(fd_node);

#ifdef USE_STL_MAP
    int oevents = this->fdMap[fd]->events;
#else
    int oevents = this->fdarray[fd]->events;
#endif
	
    if (events == oevents)
        return fd;

    int n = 0;
    struct kevent kqueueEvent[2];
	
    int addEvents = events & ~oevents;
    int deleteEvents = ~events & oevents;
	    
    if (addEvents & FDEVENT_IN)
    {
    	EV_SET(&kqueueEvent[n], fd, EVFILT_READ, EV_ADD|EV_CLEAR/*|EV_CLEAR| EV_EOF behave like select() or poll()*/, 0, 0, NULL);
		n++;
    }
    else if (deleteEvents & FDEVENT_IN)
    {
	EV_SET(&kqueueEvent[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
	n++;
    }
	
    if (addEvents & FDEVENT_OUT)
    {
        EV_SET(&kqueueEvent[n], fd, EVFILT_WRITE, EV_ADD|EV_CLEAR, 0, 0, NULL);
	n++;
    }
    else if (deleteEvents & FDEVENT_OUT)
    {
	EV_SET(&kqueueEvent[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
	n++;
    }
	
    if (0 == n)
        return fd;
	
    struct timespec ts;	
    ts.tv_sec  = 0;
    ts.tv_nsec = 0;
	
    if (kevent(this->kQueueFd,kqueueEvent, n,NULL, 0,&ts) == -1)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventKQueue::setEvent failed: %s ", __FILE__, __LINE__,strerror(errno));
	return -1;
    }
	
    return fd;
	
}

int CFdEventKQueue::deleteEvent(int fd_node, int fd )
{
    if (fd_node < 0)
        return 0;
		
    int n = 0;
    struct kevent kqueueEvent[2];
 	
#ifdef USE_STL_MAP
    int oevents = this->fdMap[fd]->events;
#else
    int oevents = this->fdarray[fd]->events;
#endif

    if (oevents & FDEVENT_IN)
    {
	EV_SET(&kqueueEvent[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
	n++;
    }

    if (oevents & FDEVENT_OUT)
    {
	EV_SET(&kqueueEvent[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
	n++;
    }

    if (0 == n)
        return 0;

    struct timespec ts;
    ts.tv_sec  = 0;
    ts.tv_nsec = 0;

    if (kevent(this->kQueueFd,(struct kevent*)&kqueueEvent, n,NULL, 0,&ts) == -1)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventKQueue::deleteEvent failed: %s ", __FILE__, __LINE__,strerror(errno));
	return 0;
    }

    return 1;
}

int CFdEventKQueue::poll(int timeoutMs)
{
    int ret;
    struct timespec ts;
	
    ts.tv_sec  = timeoutMs / 1000;
    ts.tv_nsec = (timeoutMs % 1000) * 1000000;

    if (timeoutMs >-1)
	ret = kevent(this->kQueueFd,NULL, 0,this->kQueueResults, this->maxfds,&ts);
    else
	ret = kevent(this->kQueueFd,NULL, 0,this->kQueueResults, this->maxfds,NULL); /*wait indefinitely*/

    if (ret == -1)
    {
	switch(errno)
        {
            case EINTR:
            /*
		A signal was delivered before the timeout expired and
		before any events were placed on the kqueue for	return.					
            */
                return 0;
				
            default:
                util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventKQueue::poll failed: %s ", __FILE__, __LINE__,strerror(errno));
	}
    }

    return ret;
}

int CFdEventKQueue::getEventRevent(size_t node)
{
    int events = 0, e = this->kQueueResults[node].filter;

    if (e == EVFILT_READ)
		events |= FDEVENT_IN;
    else if (e == EVFILT_WRITE)
		events |= FDEVENT_OUT;

    e = this->kQueueResults[node].flags;

    if (e & EV_EOF)
	events |= FDEVENT_HUP;
    if (e & EV_ERROR)
	events |= FDEVENT_ERR;

    return events;
}

int CFdEventKQueue::getEventFileDescriptor(size_t node)
{
    return this->kQueueResults[node].ident;
}

int CFdEventKQueue::getEventNextFileDescriptor(int node)
{
    return (node < 0) ? 0 : node + 1;
}

int CFdEventKQueue::reset()
{
    if (-1 == (this->kQueueFd = kqueue()))
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventKQueue::reset failed: %s ", __FILE__, __LINE__,strerror(errno));
	return -1;
    }

    return 0;
}

}

#endif
